﻿/*********************************************************
 * 开发人员：小兵
 * 创建时间：2012-10-9 9:09:26
 * 描述说明：
 * 
 * 更改历史：
 * 
 * *******************************************************/
namespace Mozlite.Data
{
    using System;
    using System.Web;
    using System.Data;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;


    /// <summary>
    /// SQL语句解析辅助类。
    /// </summary>
    public class SqlParser
    {
        private char[] srcTokens;
        private char[] destTokens;

        /// <summary>
        /// 字符串分隔符。
        /// </summary>
        public virtual char StringDelimiter
        {
            get { return '\''; }
        }

        /// <summary>
        /// 参数起始符号。
        /// </summary>
        public virtual char ParamToken
        {
            get { return '@'; }
        }

        /// <summary>
        /// 初始化类<see cref="SqlParser"/>。
        /// </summary>
        public SqlParser()
            : this(null, null)
        { }

        /// <summary>
        /// 通过特殊替换规则初始化类<see cref="SqlParser"/>。
        /// </summary>
        /// <param name="srcTokens">替换原始符号数组。</param>
        /// <param name="destTokens">目标替换字符集合。</param>
        public SqlParser(char[] srcTokens, char[] destTokens)
        {
            if((srcTokens == null && destTokens != null)
                || (srcTokens != null && destTokens == null)
                || (srcTokens != null && destTokens != null && srcTokens.Length != destTokens.Length))
                throw new ArgumentException("srcTokens and destTokens dimensions do not match");

            this.srcTokens = srcTokens;
            this.destTokens = destTokens;
        }

        /// <summary>
        /// 解析SQL语句，并实行替换。
        /// </summary>
        /// <param name="query">查询语句。</param>
        /// <returns>替换修改后的语句。</returns>
        /// <remarks>
        /// 默认直接返回当前SQL语句，子类重写进行替换。
        /// </remarks>
        public virtual string Parse(string query)
        {
            return query;
        }
        
        /// <summary>
        /// 替换关键字，如：GETDATE和NOW等等，所有替换在转换大写后进行。
        /// </summary>
        /// <param name="query">SQL语句。</param>
        /// <returns>返回替换后的字符串。</returns>
        protected virtual string SyntaxReplace(string query)
        {
            return query;
        }

        /// <summary>
        /// 将参数替换他们的值。
        /// </summary>
        /// <param name="commandText">SQL语句。</param>
        /// <param name="parameters">参数集合。</param>
        /// <returns>返回替换后的SQL语句。</returns>
        public virtual string ApplyParameters(string commandText, IParameter[] parameters)
        {
            // 没有参数，返回原SQL语句
            if(parameters.Length == 0)
                return commandText;
            // 通过参数建立SQL语句
            StringBuilder result = new StringBuilder(commandText.Length);
            for(int i = 0; i < commandText.Length; i++)
            {
                // 判断是否为参数
                if(commandText[i] == ParamToken)
                {
                    /* 读取参数名称，直到下一个空格或结束 */
                    int paramNameStart = i;
                    // 移动到参数结束符
                    while(++i < commandText.Length && char.IsLetterOrDigit(commandText[i]))
                        ;
                    // 获取参数名称
                    string paramName = commandText.Substring(paramNameStart, i - paramNameStart).ToLower();
                    /* 获取参数值 */
                    string paramValue = string.Empty;
                    // 获取参数为一值
                    for(int p = 0; p < parameters.Length && paramValue.Length == 0; p++)
                        if(parameters[p].ParameterName.ToLower() == paramName)
                            paramValue = parameters[p].Value.ToString();
                    /* 附加参数值 */
                    result.Append('\'').Append(paramValue.Replace("'", "''")).Append('\'');
                    // 判断是否结束
                    if(i >= commandText.Length)
                        break;
                }
                // 附加语句
                result.Append(commandText[i]);
            }
            // 添加结束符
            result.Append(';');
            return result.ToString();
        }

        /// <summary>
        /// 将ID列进行替换。
        /// </summary>
        /// <param name="query">SQL查询语句。</param>
        /// <returns>返回替换后的SQL语句。</returns>
        public virtual string ReplaceIdentifierTokens(string query)
        {
            if(srcTokens == null || destTokens == null || srcTokens.Length == 0 || destTokens.Length == 0)
                return query;

            // 第一个匹配的地方
            bool noMatch = true;
            int pos;
            for(pos = 0; pos < query.Length && noMatch; pos++)
                foreach(char srcToken in srcTokens)
                    if(query[pos] == srcToken)
                        noMatch = false;

            // 如果没有匹配，返回原语句
            if(noMatch)
                return query;

            // put pos back at the match of the source token
            pos--;

            // if there's a quote before the token, it could be inside of a string
            for(int i = 0; i < pos; i++)
                if(query[i] == StringDelimiter)
                    // start at the beginning of the string
                    pos = i;
            // loop through all string characters
            char[] queryTokens = query.ToCharArray();
            do
            {
                char token = queryTokens[pos];
                // if begin of string, advance to end so we don't replace inside string literals
                if(token == StringDelimiter)
                    pos = FindStringEndPosition(query, pos);
                else
                {
                    // if we recognize a source token, replace it by a new one
                    for(int i = 0; i < srcTokens.Length; i++)
                        if(token == srcTokens[i])
                        {
                            queryTokens[pos] = destTokens[i];
                            break;
                        }
                }
            } while(++pos < query.Length);
            // return new query
            return new string(queryTokens);
        }

        /// <summary>
        /// 将SQL语句转换为大写格式。
        /// </summary>
        /// <param name="query">SQL查询语句。</param>
        /// <returns>返回替换后的SQL语句。</returns>
        public virtual string UppercaseIdentifiers(string query)
        {
            if(string.IsNullOrEmpty(query))
                return string.Empty;

            StringBuilder replacedQuery = new StringBuilder(query.Length);

            int partStartPos = 0;
            for(int currentPos = 0; currentPos < query.Length; currentPos++)
            {
                // 判断是否为字符串
                if(query[currentPos] == StringDelimiter)
                {
                    // 将非字符串的语句转换为大写
                    replacedQuery.Append(query.Substring(partStartPos, currentPos - partStartPos).ToUpper());

                    // 字符串不转换大写
                    int endStringPos = FindStringEndPosition(query, currentPos);
                    replacedQuery.Append(query.Substring(currentPos, endStringPos - currentPos));

                    currentPos = partStartPos = endStringPos;
                }
            }
            // 剩余语句转换为大写
            replacedQuery.Append(query.Substring(partStartPos).ToUpper());

            return SyntaxReplace(replacedQuery.ToString());
        }

        /// <summary>
        /// 查找字符串结尾位置。
        /// </summary>
        /// <param name="query">SQL语句。</param>
        /// <param name="startPos">字符串起始位置。</param>
        /// <returns>字符串结尾位置。</returns>
        protected virtual int FindStringEndPosition(string query, int startPos)
        {
            startPos++;

            while(true)
            {
                // 一直移到下一个分隔符
                while(query[startPos++] != StringDelimiter)
                    ;
                // 如果到达末尾，则返回
                if(startPos == query.Length)
                    return startPos - 1;
                // 判断下一个字符是否为“'”，如果不是则就是结尾字符
                if(query[startPos++] != StringDelimiter)
                    return startPos - 2;
            }
        }
    }
}
